package hibernatemock;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import lombok.Setter;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

@SuppressWarnings("unchecked") public class HibernateMock {

	@Setter private TreeProcessor treeProcessor;
	@Setter private Persistence persistence;
	@Setter private SetupBuilder setupBuilder;

	private HibernateMock() {

	}

	/* API */

	public static HibernateMock create(EntityManager entityManager) {
		return realCreate(((Session) entityManager.getDelegate()));
	}

	public static HibernateMock create(Session session) {
		return realCreate(session);
	}

	public static HibernateMock create(EntityManagerFactory entityManagerFactory) {
		return create(entityManagerFactory.createEntityManager());
	}

	public static HibernateMock create(SessionFactory sessionFactory) {
		return create(sessionFactory.openSession());
	}

	public HibernateMock when(Class<?>... classes) {
		setupBuilder.when(classes);
		return this;
	}

	public HibernateMock deliver(Object instance) {
		setupBuilder.deliver(instance);
		return this;
	}

	public HibernateMock deliver(Object... fieldsAndValues) {
		setupBuilder.deliverWithVarArgs(fieldsAndValues);
		return this;
	}

	public HibernateMock deliver(FieldHandler fieldHandler) {
		setupBuilder.deliver(fieldHandler);
		return this;
	}

	// 2.0
	// public void deliverNull() {
	// setupBuilder.deliver(null);
	// }

	public synchronized <T> T any(Class<T> clazz) {
		return trueAny(clazz);
	}

	public synchronized <T> List<T> anyList(Class<T> clazz) {
		List<T> list = new ArrayList<T>();
		for (Setup setup : setupBuilder.getSetups())
			if (setup.getClazzField() == clazz)
				list.add(trueAny(clazz));
		return list;
	}

	// 2.0
	// public void setDeep(boolean deep) {
	// treeProcessor.setDeep(deep);
	// }

	/* PRIVATE */

	private static HibernateMock realCreate(Session session) {
		return Builder.create(session, new HibernateMock());
	}

	private <T> T trueAny(Class<T> clazz) {
		try {
			T rootEntity = (T) treeProcessor.processTree(clazz, Arrays.asList(clazz.getDeclaredAnnotations()));
			if (!persistence.session.getTransaction().isActive())
				persistence.session.getTransaction().begin();
			persistence.persist(treeProcessor.getEntitiesToPersist());
			treeProcessor.getEntitiesToPersist().clear();
			treeProcessor.getAvoidCyclic().clear();
			persistence.session.getTransaction().commit();
			return rootEntity;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}
